//
//  matrix4x4.cpp
//  GLDemo
//
//  Created by wneqi on 14-6-6.
//  Copyright (c) 2014年 xiewneqi. All rights reserved.
//

#include "matrix4x4.h"
#include "vector3.h"

const Matrix4x4 Matrix4x4::identity (Vector4 (1, 0, 0, 0),
                          Vector4 (0, 1, 0, 0),
                          Vector4 (0, 0, 1, 0),
                          Vector4 (0, 0, 0, 1));

const Matrix4x4 Matrix4x4::zero;

Matrix4x4::Matrix4x4 ()
{
    // do nothing
}

Matrix4x4::Matrix4x4 (Vector4 row0, Vector4 row1, Vector4 row2, Vector4 row3)
{
    m_rowVectors[0] = row0;
    m_rowVectors[1] = row1;
    m_rowVectors[2] = row2;
    m_rowVectors[3] = row3;
}

Matrix4x4::~Matrix4x4 ()
{
    // do nothing
}

Matrix4x4 Matrix4x4::GetInverse () const
{
    GLfloat determinant = GetDeterminant ();
    if (determinant == 0)
    {
        throw "行列式为0的正矩阵不存在逆矩阵";
    }
    Matrix4x4 cofactorMatrix;
    for (int i = 0; i < 4; ++ i)
    {
        for (int j = 0; j < 4; ++ j)
        {
            cofactorMatrix[i][j] = GetCofactor (i, j);
        }
    }
    Matrix4x4 adjustMatrix = cofactorMatrix.GetTranspose ();
    return adjustMatrix / determinant;
}

Matrix4x4 Matrix4x4::GetTranspose () const
{
    Matrix4x4 transpose;
    for (int i = 0; i < 4; ++ i)
    {
        transpose.SetColumn (i, GetRow (i));
    }
    return transpose;
}

Matrix4x4 Matrix4x4::GetCofactorMatrix () const
{
    Matrix4x4 cofactorMatrix;
    for (int i = 0; i < 4; ++ i)
    {
        for (int j = 0; j < 4; ++ j)
        {
            cofactorMatrix[i][j] = GetCofactor (i, j);
        }
    }
    return cofactorMatrix;
}

Matrix4x4 Matrix4x4::GetAdjustMatrix () const
{
    return GetCofactorMatrix ().GetTranspose ();
}

GLfloat Matrix4x4::GetDeterminant () const
{
    GLfloat result = 0;
    const Vector4 (&m)[4] = m_rowVectors;
    result = m[0][0] * GetCofactor (0, 0)
           + m[0][1] * GetCofactor (0, 1)
           + m[0][2] * GetCofactor (0, 2)
           + m[0][3] * GetCofactor (0, 3);
    return result;
}

GLfloat Matrix4x4::GetMinorDeterminant (int rowIndex, int colIndex) const
{
    // 构造列向量
    Vector3 columns[3];
    int currentColIndex = 0, currentRowIndex = 0;
    for (int i = 0; i < 4; ++ i)
    {
        if (i == colIndex) continue;
        currentRowIndex = 0;
        for (int j = 0; j < 4; ++ j)
        {
            if (j == rowIndex) continue;
            columns[currentColIndex][currentRowIndex] = m_rowVectors[j][i];
            currentRowIndex ++;
        }
        currentColIndex ++;
    }
    // 3阶矩阵的行列式可以用列向量的内积/外积获得
    return Vector3::Dot (Vector3::Cross (columns[0], columns[1]), columns[2]);
}

GLfloat Matrix4x4::GetCofactor (int rowIndex, int colIndex) const
{
    return ((rowIndex + 1 + colIndex + 1) % 2 == 0 ? 1 : -1) * GetMinorDeterminant (rowIndex, colIndex);
}

bool Matrix4x4::IsIdentity () const
{
    return ((*this) == Matrix4x4::identity);
}

Vector4 Matrix4x4::GetColumn (int colIndex) const
{
    Vector4 result (m_rowVectors[0][colIndex], m_rowVectors[1][colIndex], m_rowVectors[2][colIndex], m_rowVectors[3][colIndex]);
    return result;
}

Vector4 Matrix4x4::GetRow (int rowIndex) const
{
    return m_rowVectors[rowIndex];
}

void Matrix4x4::SetColumn (int colIndex, Vector4 column)
{
    for (int i = 0; i < 4; ++ i)
    {
        m_rowVectors[i][colIndex] = column[i];
    }
}

void Matrix4x4::SetRow (int rowIndex, Vector4 row)
{
    m_rowVectors[rowIndex] = row;
}

Vector4& Matrix4x4::operator[] (int rowIndex)
{
    return m_rowVectors[rowIndex];
}

const Vector4& Matrix4x4::operator [] (int rowIndex) const
{
    return m_rowVectors[rowIndex];
}

Matrix4x4 Matrix4x4::operator * (const Matrix4x4& m) const
{
    Matrix4x4 result;
    for (int i = 0; i < 4; ++ i)
    {
        for (int j = 0; j < 4; ++ j)
        {
            result[i][j] = Vector4::Dot ((*this)[i], m.GetColumn (j));
        }
    }
    
    return result;
}

Matrix4x4 Matrix4x4::operator / (GLfloat s) const
{
    Matrix4x4 result;
    for (int i = 0; i < 4; ++ i)
    {
        result.SetRow (i, m_rowVectors[i] / s);
    }
    
    return result;
}

Matrix4x4 Matrix4x4::operator + (const Matrix4x4& m) const
{
    return Matrix4x4 (m_rowVectors[0] + m.m_rowVectors[0],
                      m_rowVectors[1] + m.m_rowVectors[1],
                      m_rowVectors[2] + m.m_rowVectors[2],
                      m_rowVectors[3] + m.m_rowVectors[3]);
}

Matrix4x4 Matrix4x4::operator - (const Matrix4x4& m) const
{
    return Matrix4x4 (m_rowVectors[0] - m.m_rowVectors[0],
                      m_rowVectors[1] - m.m_rowVectors[1],
                      m_rowVectors[2] - m.m_rowVectors[2],
                      m_rowVectors[3] - m.m_rowVectors[3]);
}

bool Matrix4x4::operator == (const Matrix4x4& m) const
{
    return m_rowVectors[0] == m.m_rowVectors[0]
        && m_rowVectors[1] == m.m_rowVectors[1]
        && m_rowVectors[2] == m.m_rowVectors[2]
        && m_rowVectors[3] == m.m_rowVectors[3];
}

bool Matrix4x4::operator != (const Matrix4x4& m) const
{
    return !(*this == m);
}

std::ostream& operator << (std::ostream& stream, const Matrix4x4& m)
{
    return stream << "[ row0 = " << m.m_rowVectors[0] << ", "
                  << "row1 = " << m.m_rowVectors[1] << ", "
                  << "row2 = " << m.m_rowVectors[2] << ", "
                  << "row3 = " << m.m_rowVectors[3] << " ]";
}